<template>
  <div class="layer-wrap clearfix"
       ref="layer"
       v-clickOUtSide="close">
    <div @click="clickHandler">
      <slot name="button"></slot>
    </div>
    <template v-if="open">
      <transition name="fade">
        <div :class="['layer-body',{'shadow': shadow}]"
             ref="body"
             v-show="value"
             v-transfer-dom
             :data-transfer="transfer"
             :style="{left: axisX+'px', top: axisY+'px', zIndex: zIndex}">
          <div class="body-main">
            <ScrollBar :vertical="true"
                       ref="bar">
              <slot name="body">
              </slot>
            </ScrollBar>
          </div>
          <div>
            <slot name="footer"></slot>
          </div>
        </div>
      </transition>
    </template>
    <template v-else>
      <collapseTransition>
        <slot name="body"></slot>
      </collapseTransition>
    </template>
  </div>
</template>
<script>
import transferDom from '@/directives/transfer-dom';
import clickOUtSide from '@/directives/click-out';
import { on, off } from '@/common/dom';
import collapseTransition from '@/element/collapseTransition';
import ScrollBar from '@/element/scrollbar';
export default {
	components: {
		collapseTransition,
		ScrollBar,
	},
	props: {
		value: Boolean,
		placeX: {
			type: String,
			default: 'center',
		},
		placeY: {
			type: String,
			default: 'end',
		},
		offsetX: {
			type: Number,
			default: 0,
		},
		offsetY: {
			type: Number,
			default: 0,
		},
		transfer: {
			type: Boolean,
			default: true,
		},
		shadow: Boolean,
		space: {
			type: Number,
			default: 5,
		},
		autoDirection: Boolean,
		zIndex: {
			type: Number,
			default: 1000,
		},
		open: {
			type: Boolean,
			default: true,
		},
		bodySet: {
			type: Boolean,
			default: true,
		},
	},
	directives: { transferDom, clickOUtSide },
	data() {
		return {
			axisX: 0,
			axisY: 0,
			show: false,
			initY: 0,
			initX: 0,
			list: [],
			bodyWidth: 0,
			bodyHeight: 0,
			timer: null,
			body_height: 0,
		};
	},
	watch: {
		value() {
			if (!this.open) return;
			if (this.value) {
				this.$emit('on-show');
			} else {
				this.$emit('on-hidden');
			}
			this.$nextTick(() => {
				if (this.value && this.transfer) this.setPosition();
				if (this.value && !this.transfer) this.setCommonPosition();
				if (!this.value && this.transfer) {
					this.removeEvent();
				}
			});
			if (this.open && this.value) {
				setTimeout(() => {
					this.body_height = this.$refs.body.getBoundingClientRect().height;
					this.setBodyHeight();
				}, 600);
			}
			if (this.open && !this.value) {
				setTimeout(() => {
					this.body_height = 0;
					this.$refs.body.style.height = 'auto';
				}, 600);
			}
		},
		placeX() {
			if (this.value && this.transfer) {
				this.removeEvent();
				this.setPosition();
			}
			if (this.value && !this.transfer) this.setCommonPosition();
		},
		placeY() {
			if (this.value && this.transfer) {
				this.removeEvent();
				this.setPosition();
			}
			if (this.value && !this.transfer) this.setCommonPosition();
		},
	},
	methods: {
		clickHandler(e) {},
		setBodyHeight() {
			if (this.open && this.value && this.bodySet) {
				let top = this.$refs.body.getBoundingClientRect().top;
				let h = document.documentElement.clientHeight || document.body.clientHeight;
				if (h - top <= this.body_height) {
					this.$refs.body.style.height = h - top + 'px';
					this.$emit('bodyHeight', h - top);
					this.$refs.bar.resize();
				} else {
					this.$refs.body.style.height = 'auto';
					this.$emit('bodyHeight', this.body_height);
					this.$refs.bar.resize();
				}
			}
		},
		close(e) {
			if (!this.open) return;
			if (e.target.classList[0] === 'bk-select-item') return;
			this.$emit('input', false);
		},
		//获取组件本身相对文档和相对窗口位置
		getPosition(element) {
			var dc = document,
				rec = element.getBoundingClientRect(),
				_x = rec.left,
				_y = rec.top;
			_x += dc.documentElement.scrollLeft || dc.body.scrollLeft;
			_y += dc.documentElement.scrollTop || dc.body.scrollTop;
			return { elX: _x, elY: _y, rec };
		},
		//开启transfer模式下设置body插槽位置
		setPosition() {
			const el = this.$refs.layer;
			let { elX, elY, rec } = this.getPosition(el);
			let elWidth = el.offsetWidth;
			let elHeight = el.offsetHeight;
			const body = this.$refs.body;
			this.bodyWidth = body.offsetWidth;
			this.bodyHeight = body.offsetHeight;
			this.addEvent();
			switch (this.placeY) {
				case 'start':
					this.axisY = this.initY = elY - this.bodyHeight - this.space + this.offsetY;
					break;
				case 'top':
					this.axisY = this.initY = elY + this.offsetY;
					break;
				case 'center':
					this.axisY = this.initY = elY + elHeight / 2 - this.bodyHeight / 2 + this.offsetY;
					break;
				case 'bottom':
					this.axisY = this.initY = elY + elHeight - this.bodyHeight + this.offsetY;
					break;
				case 'end':
					this.axisY = this.initY = elY + elHeight + this.space + this.offsetY;
					break;
			}

			switch (this.placeX) {
				case 'start':
					this.axisX = this.initX = elX - this.bodyWidth - this.space + this.offsetX;
					break;
				case 'left':
					this.axisX = this.initX = elX + this.offsetX;
					break;
				case 'center':
					this.axisX = this.initX = parseInt(elX + elWidth / 2 - this.bodyWidth / 2 + this.offsetX);
					break;
				case 'right':
					this.axisX = this.initX = elX + elWidth - this.bodyWidth + this.offsetX;
					break;
				case 'end':
					this.axisX = this.initX = rec.right + this.space + this.offsetX;
					break;
			}
			if (this.autoDirection) this.direction(elHeight, elWidth, elX, elY, rec);
		},
		//普通模式下设置body插槽位置
		setCommonPosition() {
			const el = this.$refs.layer;
			let { elX, elY, rec } = this.getPosition(el);
			let elWidth = el.offsetWidth;
			let elHeight = el.offsetHeight;
			const body = this.$refs.body;
			this.bodyWidth = body.offsetWidth;
			this.bodyHeight = body.offsetHeight;
			switch (this.placeY) {
				case 'start':
					this.axisY = -(this.bodyHeight + this.space + this.offsetY);
					break;
				case 'top':
					this.axisY = 0 + this.offsetY;
					break;
				case 'center':
					this.axisY = elHeight / 2 - this.bodyHeight / 2 + this.offsetY;
					break;
				case 'bottom':
					this.axisY = elHeight - this.bodyHeight + this.offsetY;
					break;
				case 'end':
					this.axisY = elHeight + this.space + this.offsetY;
					break;
			}
			switch (this.placeX) {
				case 'start':
					this.axisX = -(this.bodyWidth + this.space + this.offsetX);
					break;
				case 'left':
					this.axisX = 0;
					break;
				case 'center':
					this.axisX = elWidth / 2 - this.bodyWidth / 2 + this.offsetX;
					break;
				case 'right':
					this.axisX = elWidth - this.bodyWidth + this.offsetX;
					break;
				case 'end':
					this.axisX = elWidth + this.space + this.offsetX;
					break;
			}
			if (this.autoDirection) this.direction(elHeight, elWidth, elX, elY, rec);
		},
		//方向自动改变判断
		direction(elHeight, elWidth, elX, elY, rec) {
			if (this.placeY === 'start') {
				if (rec.top < this.bodyHeight + this.space) {
					if (this.transfer) this.axisY = this.initY = elY + elHeight + this.space;
					if (!this.transfer) this.axisY = elHeight + this.space;
				}
			}
			if (this.placeY === 'end') {
				if (document.body.clientHeight - rec.bottom - this.space < this.bodyHeight) {
					if (this.transfer) this.axisY = this.initY = elY - this.bodyHeight - this.space;
					if (!this.transfer) this.axisY = -(this.bodyHeight + this.space);
				}
			}
			if (this.placeX === 'start') {
				if (rec.left - this.space < this.bodyWidth) {
					if (this.transfer) this.axisX = this.initX = rec.right + this.space;
					if (!this.transfer) this.axisX = elWidth + this.space;
				}
			}
			if (this.placeX === 'end') {
				if (document.body.clientWidth - rec.right - this.space < this.bodyWidth) {
					if (this.transfer) this.axisX = this.initX = elX - this.bodyWidth - this.space;
					if (!this.transfer) this.axisX = -(this.bodyWidth + this.space);
				}
			}
		},
		//当有父级元素滚动时实时改变想X,Y值
		scrollHandler(e) {
			let item;
			for (let v of this.list) {
				if (v.el === e.target) {
					item = v;
					break;
				}
			}
			this.axisY = this.initY - (e.target.scrollTop - item.y);
			this.axisX = this.initX - (e.target.scrollLeft - item.x);
		},
		//给所有父级添加滚动事件
		addEvent() {
			let node = this.$el;
			this.list = [];
			while (node.tagName !== 'BODY') {
				on(node.parentNode, 'scroll', this.scrollHandler);
				this.list.push({ el: node.parentNode, y: node.parentNode.scrollTop, x: node.parentNode.scrollLeft });
				node = node.parentNode;
			}
		},
		//移除所有父级滚动事件
		removeEvent() {
			let node = this.$el;
			while (node.tagName !== 'BODY') {
				off(node.parentNode, 'scroll', this.scrollHandler);
				node = node.parentNode;
			}
		},
		//当窗口改变时重新计算位置
		resizeHandler() {
			if (!this.open) return;
			clearTimeout(this.timer);
			this.timer = setTimeout(() => {
				this.resize();
			}, 100);
		},
		resize() {
			if (this.value && this.transfer) {
				this.removeEvent();
				this.setPosition();
			}
			if (this.value && !this.transfer) this.setCommonPosition();
		},
	},
	updated() {
		if (!this.open) return;
		//当body内容大小改变时重新计算位置
		if (!this.show) return;
		if (
			(this.bodyHeight && Math.abs(this.$refs.body.offsetHeight - this.bodyHeight) > 1) ||
			(this.bodyWidth && Math.abs(this.$refs.body.offsetWidth - this.bodyWidth) > 1)
		) {
			this.resize();
		}
	},
	mounted() {
		if (!this.open) return;
		on(window, 'resize', this.resizeHandler);
		on(window, 'resize', this.setBodyHeight);
	},
	destroyed() {
		if (!this.open) return;
		off(window, 'resize', this.resizeHandler);
		off(window, 'resize', this.setBodyHeight);
	},
};
</script>
<style lang="less">
.layer-wrap {
	position: relative;
}

.layer-body {
	position: absolute;
	z-index: 1000;
	display: flex;
	flex-direction: column;
	.body-main {
		flex: 1;
		overflow: hidden;
	}
	&.shadow {
		box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
	}
}

.fade-enter-active,
.fade-leave-active {
	transition: opacity 0.6s;
}

.fade-enter,
.fade-leave-to {
	opacity: 0;
}
</style>
